package com.scopely.infrastructure.kinesis;
import com.amazonaws.AmazonClientException;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.kinesis.AmazonKinesis;
import com.amazonaws.services.kinesis.AmazonKinesisClient;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.util.IOUtils;
import org.fest.assertions.core.Condition;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Observable;
import rx.observers.TestSubscriber;
import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
public class KinesisRecorderTest {
private static final Logger LOGGER = LoggerFactory.getLogger(KinesisRecorderTest.class);
private AmazonDynamoDB dynamoDb;
private String kinesisStreamName;
private String bucketName;
private AmazonS3 s3;
private AmazonKinesis kinesis;
private AWSCredentialsProvider awsCredentialsProvider;
private ExecutorService executorService;
@Before
public void setUp() throws Exception {
awsCredentialsProvider = new DefaultAWSCredentialsProviderChain();
kinesis = new AmazonKinesisClient(awsCredentialsProvider);
dynamoDb = new AmazonDynamoDBClient(awsCredentialsProvider);
executorService = Executors.newSingleThreadExecutor();
kinesisStreamName = "krt-test-" + UUID.randomUUID().toString();
kinesis.createStream(kinesisStreamName, 1);
while (!"ACTIVE".equals(kinesis.describeStream(kinesisStreamName).getStreamDescription().getStreamStatus())) {
LOGGER.info("Waiting for stream…");
Thread.sleep(1000);
}
bucketName = kinesisStreamName + UUID.randomUUID().toString().substring(0, 4);
s3 = new AmazonS3Client(awsCredentialsProvider);
s3.createBucket(bucketName);
while (!s3.doesBucketExist(bucketName)) {
LOGGER.info("Waiting for bucket {} …", bucketName);
Thread.sleep(1000);
}
}
@After
public void tearDown() throws Exception {
try {
s3.listObjects(bucketName).getObjectSummaries().forEach(summary -> {
s3.deleteObject(summary.getBucketName(), summary.getKey());
});
} catch (Exception e) {
LOGGER.warn("Failed to delete objects from s3. Ignoring on tearDown: ", e);
}
try {
s3.deleteBucket(bucketName);
} catch (Exception e) {
LOGGER.warn("Failed to delete bucket " + bucketName + ". Ignoring on tearDown: ", e);
}
try {
kinesis.deleteStream(kinesisStreamName);
} catch (AmazonClientException e) {
LOGGER.warn("Failed to delete stream " + kinesisStreamName + ". Ignoring on tearDown: ", e);
}
try {
dynamoDb.deleteTable("kinesis-recorder-" + kinesisStreamName);
} catch (Exception e) {
LOGGER.warn("Failed to delete kinesis leasing table kinesis-recorder-" + kinesisStreamName + ". Ignoring on tearDown: ", e);
}
executorService.shutdown();
executorService.awaitTermination(10, TimeUnit.SECONDS);
}
@Test
public void testRecordAndReplay() throws Exception {
VcrConfiguration configuration = new VcrConfiguration(kinesisStreamName, kinesisStreamName, bucketName,
1024 * 10, TimeUnit.SECONDS.toMillis(60));
KinesisRecorder recorder = new KinesisRecorder(configuration, s3, awsCredentialsProvider);
executorService.submit(recorder);
kinesis.putRecord(kinesisStreamName, ByteBuffer.wrap(new byte[40_000]), UUID.randomUUID().toString());
kinesis.putRecord(kinesisStreamName, ByteBuffer.wrap(new byte[40_000]), UUID.randomUUID().toString());
kinesis.putRecord(kinesisStreamName, ByteBuffer.wrap(new byte[40_000]), UUID.randomUUID().toString());
kinesis.putRecord(kinesisStreamName, ByteBuffer.wrap(new byte[40_000]), UUID.randomUUID().toString());
kinesis.putRecord(kinesisStreamName, ByteBuffer.wrap(new byte[40_000]), UUID.randomUUID().toString());
kinesis.putRecord(kinesisStreamName, ByteBuffer.wrap(new byte[40_000]), UUID.randomUUID().toString());
kinesis.putRecord(kinesisStreamName, ByteBuffer.wrap(new byte[40_000]), UUID.randomUUID().toString());
kinesis.putRecord(kinesisStreamName, ByteBuffer.wrap(new byte[40_000]), UUID.randomUUID().toString());
kinesis.putRecord(kinesisStreamName, ByteBuffer.wrap(new byte[40_000]), UUID.randomUUID().toString());
kinesis.putRecord(kinesisStreamName, ByteBuffer.wrap(new byte[40_000]), UUID.randomUUID().toString());
Thread.sleep(TimeUnit.SECONDS.toMillis(45));
KinesisPlayer player = new KinesisPlayer(configuration, s3, kinesis);
Observable<byte[]> bytesObservable = player
.playableObjects(LocalDateTime.now().truncatedTo(ChronoUnit.DAYS), LocalDateTime.now().plusDays(1))
.flatMap(player::objectToPayloads);
TestSubscriber<byte[]> testSubscriber = new TestSubscriber<>();
bytesObservable.subscribe(testSubscriber);
testSubscriber.awaitTerminalEvent();
testSubscriber.assertNoErrors();
List<byte[]> result = testSubscriber.getOnNextEvents();
assertThat(result).isNotEmpty();
assertThat(result).are(new Condition<byte[]>() {
@Override
public boolean matches(byte[] value) {
return Arrays.equals(value, new byte[40_000]);
}
});
recorder.stop();
}
@Test
public void testFlushExpectedData() throws Exception {
VcrConfiguration configuration = new VcrConfiguration(kinesisStreamName, kinesisStreamName, bucketName,
1024 * 1024, TimeUnit.SECONDS.toMillis(10));
AmazonS3 surveilledS3 = spy(s3);
KinesisRecorder recorder = new KinesisRecorder(configuration, surveilledS3, awsCredentialsProvider);
executorService.submit(recorder);
kinesis.putRecord(kinesisStreamName,
ByteBuffer.wrap("String 1".getBytes(StandardCharsets.UTF_8)),
UUID.randomUUID().toString());
kinesis.putRecord(kinesisStreamName,
ByteBuffer.wrap("String 2".getBytes(StandardCharsets.UTF_8)),
UUID.randomUUID().toString());
kinesis.putRecord(kinesisStreamName,
ByteBuffer.wrap("String 3".getBytes(StandardCharsets.UTF_8)),
UUID.randomUUID().toString());
kinesis.putRecord(kinesisStreamName,
ByteBuffer.wrap("String 4".getBytes(StandardCharsets.UTF_8)),
UUID.randomUUID().toString());
Thread.sleep(TimeUnit.SECONDS.toMillis(45));
ArgumentCaptor<ByteArrayInputStream> baisCaptor = ArgumentCaptor.forClass(ByteArrayInputStream.class);
verify(surveilledS3).putObject(anyString(), anyString(), baisCaptor.capture(), any(ObjectMetadata.class));
assertThat(baisCaptor.getValue()).isNotNull();
baisCaptor.getValue().reset();
byte[] bytes = IOUtils.toByteArray(baisCaptor.getValue());
assertThat(bytes).startsWith(Base64.getEncoder().encode("String 1".getBytes(StandardCharsets.UTF_8)));
recorder.stop();
}
}